//两颗堆叠，支持到6颗device，帧率可达到51Hz。
#include <msp430.h> 
#include "TLC6983DMA.h"
#include "TLC6983.h"
#include "CCSI_Socket.h"
#include "delayTimer.h"
#include "frames.h"

#define CPU_F ((double)3000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))

const uint8_t SINGLE_SCREEN = 16;
const uint8_t STACKED_UNITS = 2;

uint8_t dmaTxData[37]; //Stores the data bytes to be sent
uint8_t dmaRxData[18]; //Stores the data bytes to be received
uint8_t img_R_RGB[SCREEN_SIZE_Y][SCREEN_SIZE_X];
uint8_t img_G_RGB[SCREEN_SIZE_Y][SCREEN_SIZE_X];
uint8_t img_B_RGB[SCREEN_SIZE_Y][SCREEN_SIZE_X];

unsigned int dmaSyncFlag; //timeout flag

/* Function Name: setData
 *
 * Purpose: Set data send buffer.
 *
 * Parameters:
 * data -> The pointer of data send buffer.
 * high, mid, low -> Data bytes, MSB first.
 * bias -> Bias in the date send buffer.
 *
 */
void setData(uint16_t *data, uint16_t high, uint16_t mid, uint16_t low, uint8_t bias){
    data[0 + bias] = high;
    data[1 + bias] = mid;
    data[2 + bias] = low;
}

void prepareFrame(uint16_t animation_cnt, uint16_t frame){
    // For now only implement NS
    uint8_t y_dir = 0; //Loop index
    uint8_t x_dir = 0;
    uint8_t x_loop_start = 0;
    uint8_t y_loop_start = 0;
    uint8_t x_loop_end = 0;
    uint8_t y_loop_end = 0;
    uint8_t x_move_step = (SINGLE_SCREEN * STACKED_UNITS); // 1 means move frame to left and replace most right with new frame
    uint8_t y_move_step = 0;
    uint8_t bits = 0; //loop for black/white image

    // When replacing the frame only part of SRAM needs to be updated
    x_loop_end = (SINGLE_SCREEN * STACKED_UNITS);
    y_loop_end = ((SINGLE_SCREEN * STACKED_UNITS) / ANIMATION_TIME) * (animation_cnt + 1);
    // When moving the frame, whole SRAM needs to be updated - will need to take care of direction to prevent "old" is already overwritten by new data
    y_loop_end = (SINGLE_SCREEN * STACKED_UNITS);

    for(y_dir = y_loop_start; y_dir < y_loop_end; y_dir++){
        for(x_dir = x_loop_start; x_dir < x_loop_end; x_dir++){
            if(x_dir >= (x_loop_end - x_move_step)){
                if(FRAME_RBG_CNT < FRAME_SIZE_X){
                    // first get index - divide by 8
                    bits = (x_dir >> 3) & 0xFF;
                    // For black/white image need to get bits
                    img_R_RGB[y_dir][x_dir] = frame_data[frame][y_dir][bits] & (0x1 << (x_dir & 0x7));
                    // For black/white colors, the data for G and B is same as R
                    img_G_RGB[y_dir][x_dir] = img_R_RGB[y_dir][x_dir];
                    img_B_RGB[y_dir][x_dir] = img_R_RGB[y_dir][x_dir];
                }
                else{
                    img_R_RGB[y_dir][x_dir] = frame_data[frame][y_dir][x_dir];
                    // For RGB frame need to get 3 different data
                    if(FRAME_RBG_CNT > FRAME_SIZE_X){
                        img_G_RGB[y_dir][x_dir] = frame_data[frame][y_dir][x_dir + FRAME_SIZE_X];
                        img_B_RGB[y_dir][x_dir] = frame_data[frame][y_dir][x_dir + FRAME_SIZE_X*2];
                    }
                    else{
                        // For 256 colors, the data for G and B is same as R
                        img_G_RGB[y_dir][x_dir] = img_R_RGB[y_dir][x_dir];
                        img_B_RGB[y_dir][x_dir] = img_R_RGB[y_dir][x_dir];
                    }
                }
            }
            else{
                img_R_RGB[y_dir][x_dir] = img_R_RGB[y_dir][x_dir + x_move_step];
                img_G_RGB[y_dir][x_dir] = img_G_RGB[y_dir][x_dir + x_move_step];
                img_B_RGB[y_dir][x_dir] = img_B_RGB[y_dir][x_dir + x_move_step];
            }
        }
    }
}
void sendSRAMandSYNC()
{
    uint16_t data[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};//Data send buffer
    uint8_t y_dir = 0; //Loop index
    uint8_t x_dir = 0;
    uint8_t k = 0;

    for(y_dir = 0 ; y_dir < (SINGLE_SCREEN * STACKED_UNITS); y_dir++){
        for(x_dir = 0 ; x_dir < SINGLE_SCREEN ; x_dir++){
            for(k = 0; k < STACKED_UNITS; k++){
                setData(data,
                        lookup_table_B[img_B_RGB[y_dir][x_dir + (SINGLE_SCREEN * k)]][1],   // B
                        lookup_table_G[img_G_RGB[y_dir][x_dir + (SINGLE_SCREEN * k)]][1],   // G
                        lookup_table_R[img_R_RGB[y_dir][x_dir + (SINGLE_SCREEN * k)]][1],   // R
                        k * 3);
            }
            CCSI_write(W_SRAM, data, STACKED_UNITS * 3);
        }
    }
    // For any repeat need to SYNC to keep displaying the frame
    CCSI_write(W_VSYNC, data, 0);
}

void sendSRAMandSYNC_Highlight()
{
    uint16_t data[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};//Data send buffer
    uint8_t k = 0;
    uint8_t i = 0;
    uint8_t j = 0;

    for(i = 0 ; i < 32 ; i++){
        for(j = 0 ; j < 16 ; j++){
            CCSI_write_sram();
            //delay_us(100);
        }
    }
    delay_us(1000);
    // For any repeat need to SYNC to keep displaying the frame
    CCSI_write(W_VSYNC, data, 0);
}

/**
 * main.c
 */
int main(void)
{
    uint16_t data[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};//Data send buffer
    uint8_t y_dir = 0; //Loop index
    uint8_t x_dir = 0;
    uint8_t k = 0;
    uint16_t current_repeat = 0;
    uint16_t current_frame = 0;
    uint16_t animation_cnt = 0;
    
    // FC3 fields
    uint16_t lod_lsd_rb = 0x1 << 7;
    uint16_t cc_r = 0x8F << 8;
    uint16_t cc_g = 0x55;
    uint16_t cc_b = 0x40 << 8;
    uint16_t bc   = 0x02;
    uint16_t lsd_rm = 0xF << 3;
    uint16_t lsdvth_r = 0x0 << 7;
    uint16_t lsdvth_g = 0x0 << 10;
    uint16_t lsdvth_b = 0x0 << 13;
    
    uint16_t fc0_0 = (LODRM_EN_1 | PDC_EN_1 | CHIP_NUM__6) & 0xFFFF;
    uint16_t fc0_1 = ((FREQ_MUL__16 | SUBP_NUM__64 | SCAN_NUM__32) >> 16) & 0xFFFF; //8 x 5MHz= 40Mhz, 64 sub-periods, totally 39.321ms, about 25.43Hz
    uint16_t fc0_2 = ((BACK_TH_1 | LINE_CHRG_1) >> 32) & 0xFFFF;
    
    uint16_t fc3_0 = lod_lsd_rb + cc_r;
    uint16_t fc3_1 = cc_g + cc_b;
    uint16_t fc3_2 = bc + lsd_rm + lsdvth_r + lsdvth_g + lsdvth_b;
    
    WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timer
    
    __bic_SR_register(GIE); //enable interrupt
    ucsInitial(); //system clock initial
    timerInitial(); //
    spiInitial();
    dmaInitial();
    
    CCSI_write(W_CHIP_INDEX, data, 0);//Write chip index.
    CCSI_write(W_CHIP_INDEX, data, 0);//Write chip index.
    
    setData(data, fc0_2, fc0_1, fc0_0, 0);//8 x 5MHz= 40Mhz, 64 sub-periods, totally 39.321ms, about 25.43Hz
    CCSI_write(W_FC0, data, 3);
    
    setData(data, 0x0045, 0xE252, 0x97F0, 0);//576 x GCLK for each segment, each sub-period is totally 409.6us
    //0X9640 for low is 576 x GCLK
    //0x0044, 0x2252, 0x95FF
    CCSI_write(W_FC1, data, 3);
    
    setData(data, 0x000D, 0xD033, 0x0000, 0);//
    CCSI_write(W_FC2, data, 3);
    
    setData(data, fc3_2, fc3_1, fc3_0, 0);//
    CCSI_write(W_FC3, data, 3);
    
    setData(data, 0x0007, 0x003F, 0x6001, 0);//
    CCSI_write(W_FC4, data, 3);
    
    // Write black frame - To prevent showing existing frames in LED Driver SRAM
    // Clear MCU SRAM frame
    for(y_dir = 0; y_dir < (SINGLE_SCREEN * STACKED_UNITS); y_dir++){
        for(x_dir = 0; x_dir < SINGLE_SCREEN; x_dir++){
            for(k = 0; k < STACKED_UNITS; k++){
                img_R_RGB[y_dir][x_dir + (SINGLE_SCREEN * k)] = 0x00;
                img_G_RGB[y_dir][x_dir + (SINGLE_SCREEN * k)] = 0x00;
                img_B_RGB[y_dir][x_dir + (SINGLE_SCREEN * k)] = 0x00;
            }
        }
    }
    
    // Send black frame to LED Driver SRAM and SYNC
    sendSRAMandSYNC();
    
    while(1){
        sendSRAMandSYNC_Highlight();
        /*for(current_frame = 0; current_frame < FRAME_CNT; current_frame++) {
            if(ANIMATION_TIME > 1){
                k = ani_time[current_frame];
            }
            else{
                k = 1;
            }
            for(animation_cnt = 0; animation_cnt < k; animation_cnt++) {
                // Prepare frame based on animation - if no animation the whole frame is replaced
                prepareFrame(animation_cnt, current_frame);
                // Send MCU SRAM fram to LED Driver SRAM and SYNC
                sendSRAMandSYNC();
            }
            // Repeat count how many times the frame is displayed. For example single pictures are displayed several seconds before moving to next pictures.
            for(current_repeat = 0; current_repeat < REPEAT_CNT; current_repeat++) {
                // Need to keep on sending the SRAM data. Otherwise the SYNC goes so fast that frames are displayed too shortly
                sendSRAMandSYNC();
            }

            for(current_repeat=0; current_repeat<k; current_repeat++){
            sendSRAMandSYNC_Highlight();
            }

        }*/
    }
    
    return 0;
}
